import cv2
import numpy as np
import serial
import time

# 串口配置
PORT = "COM3"
BAUDRATE = 115200
START_BYTE = b'\xFF'

WIDTH = 128
HEIGHT = 64

ser = serial.Serial(PORT, BAUDRATE)
time.sleep(2)

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)

def extract_hand_mask(frame):
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    lower = np.array([0, 30, 60])
    upper = np.array([20, 150, 255])
    mask = cv2.inRange(hsv, lower, upper)

    # 降噪 + 连通区域保留最大手部区域
    kernel = np.ones((3,3), np.uint8)
    mask = cv2.erode(mask, kernel, iterations=1)
    mask = cv2.dilate(mask, kernel, iterations=2)

    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    mask = np.zeros_like(mask)
    if contours:
        c = max(contours, key=cv2.contourArea)
        cv2.drawContours(mask, [c], -1, 255, thickness=cv2.FILLED)
    return mask

def mask_to_oled_bytes(mask):
    mask = cv2.resize(mask, (WIDTH, HEIGHT), interpolation=cv2.INTER_AREA)
    _, binary = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)

    result = bytearray(WIDTH * HEIGHT // 8)
    for y in range(HEIGHT):
        for x in range(WIDTH):
            if binary[y, x] == 0:
                continue
            byte_index = y * (WIDTH // 8) + (x // 8)
            bit_index = 7 - (x % 8)
            result[byte_index] |= (1 << bit_index)
    return result, binary

# 控制帧更新频率
frame_delay = 0.1  # 延迟 100ms

last_sent_time = time.time()

while True:
    ret, frame = cap.read()
    if not ret:
        continue

    hand_mask = extract_hand_mask(frame)
    oled_bytes, preview = mask_to_oled_bytes(hand_mask)

    # 检查时间间隔，确保每隔一定时间才发送数据
    if time.time() - last_sent_time > frame_delay:
        ser.write(START_BYTE + oled_bytes)
        last_sent_time = time.time()

    # 显示效果预览
    preview = cv2.resize(preview, (WIDTH * 4, HEIGHT * 4), interpolation=cv2.INTER_NEAREST)
    cv2.imshow("Hand Mask Preview", preview)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
ser.close()
